在 gradle 脚本中配置 native 层的参数

2021-03-20

我们知道在 Android 开发中,可以通过在 build.gradle 脚本中通过 buildConfigField 添加一些全局的配置参数,编译后会在 BuildConfig 中生成相应的常量供 jvm 语言调用,这样可以集中管理项目中的配置项。但是如果项目涉及到了 native 层的开发,且 native 层也有需要配置的参数,如果还是将配置项添加到 BuildConfig 中,那么只能通过 jni 去调用 java 获取:

1
2
3
4
5
6
package com.porum.myapplication;

public final class BuildConfig {
// Field from default config.
public static final String KEY = "123456";
}
1
2
3
4
jclass clazz = env->FindClass("com/porum/myapplication/BuildConfig");
jfieldID fieldId = env->GetStaticFieldID(clazz, "KEY", "Ljava/lang/String;");
jstring str = (jstring) env->GetStaticObjectField(clazz, fieldId);
const char* key = env->GetStringUTFChars(str, 0);

但是我认为这不是一个最优解,虽然 jni call java 很常见,但是这极度依赖类名,方法/属性名,签名信息。如果有一个发生变动,则 jni 层需要同步修改。

由于我们的 native 代码是使用 cmake 构建的,我们可以通过 cmake 配置构建参数:

1
2
3
set(KEY "123456")
configure_file(config.h.in ${CMAKE_BINARY_DIR}/xxx/out/config.h)
include_directories(${CMAKE_BINARY_DIR}/xxx/out)

然后在构建的时候会自动生成 config.h.in 文件和 config.h 头文件:

1
2
3
4
5
6
/**
* Automatically generated file. DO NOT MODIFY.
* https://cmake.org/pipermail/cmake/2011-March/043464.html
*/

#cmakedefine KEY "@KEY@"
1
2
3
4
5
6
/**
* Automatically generated file. DO NOT MODIFY.
* https://cmake.org/pipermail/cmake/2011-March/043464.html
*/

#define KEY "123456"

这样就可以在 native 代码里直接通过 #include "config.h" 获取配置项了。

但是此时参数是在 cmake 脚本里配置的,如果我们想在 build.gradle 中配置的话,只需要将参数传递给 cmake 即可:

1
2
3
4
5
6
7
8
9
10
android {
defaultConfig {
externalNativeBuild {
cmake {
// 参数名称前面加 -D
arguments "-DKEY=123456"
}
}
}
}

然后将 cmake 脚本改一下:

1
set(KEY ${KEY})

这样参数的传递路径就变成了:build.gradle -> CMakeLists.txt -> config.h.in -> config.h